﻿//                       MTMonB.cpp                    � 2009-09-12 Agner Fog
//Jorel Lalicki, Matthew McMullan, Mike Heise
//
//                 Multithreaded PMC Test program
// 
// The setup of the Performance Monitor Counters is microprocessor-specific.
// The specifications for PMC setup for each microprocessor family is defined
// in the tables CounterDefinitions and CounterTypesDesired.
//
// � 2000-2009 GNU General Public License www.gnu.org/licences
//////////////////////////////////////////////////////////////////////////////

#include "MSRDriver.h"
#include "MTMon.h"
#include "Config.h"
//_Include

// comment out next line if compiler doesn't support intrinsic functions:
#include <intrin.h>
#include <iostream>

#define Intrin_Read static inline void Serialize (){int dummy[4];__cpuid(dummy,0);volatile int DontSkip = dummy[0];} 

//Timing loop with dummy code to benchmark timing platform overhead
#define Benchmark_Test Serialize();ThreadData[thread].CountTemp[0] = (int)__rdtsc();Serialize();Serialize();ThreadData[thread].CountTemp[0] -= (int)__rdtsc();Serialize();


/*############################################################################
#
#        Thread data
#
############################################################################*/
__declspec(align(CACHELINESIZE))      // align by cache line size (syntax depends on compiler!)
struct SThreadData {
   // Data for each thread
   int CountTemp[MAXCOUNTERS+1];      // temporary storage of clock counts and PMC counts
   int CountOverhead[MAXCOUNTERS+1];  // temporary storage of count overhead
   int ClockResults[REPETITIONS];     // clock count results
   int PMCResults[REPETITIONS*MAXCOUNTERS]; // PMC count results
};

extern "C" {
   SThreadData ThreadData[NUM_THREADS];// Results for all threads
   int NumThreads = NUM_THREADS;       // Number of threads
   int NumCounters = 0;                // Number of valid PMC counters in Counters[]
   int MaxNumCounters = MAXCOUNTERS;   // Maximum number of PMC counters
   int UsePMC = USE_PERFORMANCE_COUNTERS;// 0 if no PMC counters used
   int *PThreadData = (int*)ThreadData;// Pointer to measured data for all threads
   int ThreadDataSize = sizeof(SThreadData);// Size of per-thread counter data block (bytes)
   // offset of clock results of first thread into ThreadData (bytes)
   int ClockResultsOS = (ThreadData[0].ClockResults-ThreadData[0].CountTemp)*sizeof(int);
   // offset of PMC results of first thread into ThreadData (bytes)
   int PMCResultsOS = (ThreadData[0].PMCResults-ThreadData[0].CountTemp)*sizeof(int);
   // counter register numbers used
   int Counters[MAXCOUNTERS] = {0};
   int EventRegistersUsed[MAXCOUNTERS] = {0};
}

//////////////////////////////////////////////////////////////////////////////
//
//    Inline functions for reading counters
//
//////////////////////////////////////////////////////////////////////////////

#ifdef __INTRIN_H_  // Use intrinsics for reading counters

Intrin_Read

#else  // Use inline assembly for reading counters

static inline void Serialize () {
   // serialize CPU
   __asm {
      xor eax, eax
      cpuid
   }
   // Note: ebx is changed by cpuid.
   // The compiler will save ebx automatically in most cases, but bugs have been observed.
}


#pragma warning(disable:4035)
static inline int __rdtsc(int nPerfCtr) {
   // read performance monitor counter number nPerfCtr
   __asm {
      mov ecx, nPerfCtr
      rdpmc
   }
}
#pragma warning(default:4035)

#endif  // __INTRIN_H_

//    Test Loop
int MonTestLoop (int thread) {
	// this function runs the code to test REPETITIONS times
	// and reads the counters before and after each run:
	int i; // counter index
	int repi; // repetition index
	for (i = 0; i < MAXCOUNTERS+1; i++) {
		ThreadData[thread].CountOverhead[i] = 0x7FFFFFFF;
	}
//Initialize variables and such needed by the test function
//_Initialize
for (repi = 0; repi < OVERHEAD_REPETITIONS; repi++){
      Serialize();
#if USE_PERFORMANCE_COUNTERS
      // Read counters
      for (i = 0; i < MAXCOUNTERS; i++) {
         ThreadData[thread].CountTemp[i+1] = (int)__readpmc(Counters[i]);
      }
#endif
Benchmark_Test
#if USE_PERFORMANCE_COUNTERS
      // Read counters
      for (i = 0; i < MAXCOUNTERS; i++) {
         ThreadData[thread].CountTemp[i+1] -= (int)__readpmc(Counters[i]);
      }
#endif
      Serialize();
      for (i = 0; i < MAXCOUNTERS+1; i++) {
         if (-ThreadData[thread].CountTemp[i] < ThreadData[thread].CountOverhead[i]) { // find minimum counts
            ThreadData[thread].CountOverhead[i] = -ThreadData[thread].CountTemp[i];
         }
      }
	}
   // Second test loop. Includes code to test.
   // This must be identical to first test loop, except for the test function
   for (repi = 0; repi < REPETITIONS; repi++){
      Serialize();
#if USE_PERFORMANCE_COUNTERS
      for (i = 0; i < MAXCOUNTERS; i++){
         ThreadData[thread].CountTemp[i+1] = (int)__readpmc(Counters[i]);// Read counters
      }
#endif

		Serialize();
		ThreadData[thread].CountTemp[0] = (int)__rdtsc();
		Serialize();

			//Here is the code to be tested
//_Function
		Serialize();
		ThreadData[thread].CountTemp[0] -= (int)__rdtsc();
		Serialize();
#if USE_PERFORMANCE_COUNTERS
      // Read counters
      for (i = 0; i < MAXCOUNTERS; i++) {
         ThreadData[thread].CountTemp[i+1] -= (int)__readpmc(Counters[i]);
      }
#endif
      Serialize();
      ThreadData[thread].ClockResults[repi] = -ThreadData[thread].CountTemp[0] - ThreadData[thread].CountOverhead[0];// subtract overhead
      for (i = 0; i < MAXCOUNTERS; i++) {
         ThreadData[thread].PMCResults[repi+i*REPETITIONS] = -ThreadData[thread].CountTemp[i+1] - ThreadData[thread].CountOverhead[i+1];
      }
   }

//_Post_Function

   return REPETITIONS;
}

